// cdxCDynamicWndEx.cpp: implementation of the cdxCDynamicWndEx class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "cdxCDynamicWndEx.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#define new DEBUG_NEW
#endif

/////////////////////////////////////////////////////////////////////////////
// Some static variables (taken from cdxCDynamicControlsManager)
/////////////////////////////////////////////////////////////////////////////

#define REGVAL_NOSTATE      -1
#define REGVAL_VISIBLE      1
#define REGVAL_HIDDEN       0
#define REGVAL_MAXIMIZED    1
#define REGVAL_ICONIC       0
#define REGVAL_INVALID      0
#define REGVAL_VALID        1

/*
 * registry value names
 * (for StoreWindowPosition()/RestoreWindowPosition())
 */

static LPCTSTR lpszRegVal_Left = _T("Left"),
               lpszRegVal_Right = _T("Right"),
               lpszRegVal_Top = _T("Top"),
               lpszRegVal_Bottom = _T("Bottom"),
               lpszRegVal_Visible = _T("Visibility"),
               lpszRegVal_State = _T("State"),
               lpszRegVal_Valid = _T("(valid)");

LPCTSTR cdxCDynamicWndEx::M_lpszAutoPosProfileSection = _T("WindowPositions");

/////////////////////////////////////////////////////////////////////////////
// cdxCDynamicWndEx
/////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////
// cdxCDynamicWndEx stretches windows
/////////////////////////////////////////////////////////////////////////////

static inline CString _makeFullProfile(LPCTSTR lpszBase, const CString& str)
{
    CString s = lpszBase;

    if (s.GetLength() && (s[s.GetLength() - 1] != _T('\\')))
    {
        s += _T('\\');
    }

    s += str;
    return s;
}

void cdxCDynamicWndEx::OnInitialized()
{
    ASSERT(IsWindow());

    if (!m_strAutoPos.IsEmpty())
    {
#if _MSC_VER < 1300   // vc6
        if (!RestoreWindowPosition(_makeFullProfile(M_lpszAutoPosProfileSection, m_strAutoPos), rflg_all))
        #else                 // vc7
            if (!RestoreWindowPosition(_makeFullProfile(M_lpszAutoPosProfileSection, m_strAutoPos), "", rflg_all))
            #endif
            {
                Window()->CenterWindow();
                StretchWindow(10);
            }
    }
}

void cdxCDynamicWndEx::OnDestroying()
{
    if (!m_strAutoPos.IsEmpty() && IsWindow())
    {
        StoreWindowPosition(_makeFullProfile(M_lpszAutoPosProfileSection, m_strAutoPos));
    }
}

/////////////////////////////////////////////////////////////////////////////
// cdxCDynamicWndEx stretches windows
/////////////////////////////////////////////////////////////////////////////

/*
 * stretches the window by szDelta (i.e. if szDelta is 100, the window is enlarged by 100 pixels)
 * stretching means that the center point of the window remains
 *
 * returns false if the window would be smaller than (1,1)
 *
 * NOTE: this function does NOT care of the min/max dimensions of a window
 *          Use MoveWindow() if you need to take care of it.
 *
 * STATIC
 */

bool cdxCDynamicWndEx::StretchWindow(const CSize& szDelta)
{
    if (!IsWindow())
    {
        ASSERT(false);
        return false;
    }

    CWnd* pWnd = Window();

    WINDOWPLACEMENT wpl;
    pWnd->GetWindowPlacement(&wpl);

    wpl.rcNormalPosition.left -= szDelta.cx / 2;
    wpl.rcNormalPosition.right += (szDelta.cx + 1) / 2;
    wpl.rcNormalPosition.top -= szDelta.cy / 2;
    wpl.rcNormalPosition.bottom += (szDelta.cy + 1) / 2;
    //  wpl.flags   =   SW_SHOWNA|SW_SHOWNOACTIVATE;

    if ((wpl.rcNormalPosition.left >= wpl.rcNormalPosition.right) || (wpl.rcNormalPosition.top >= wpl.rcNormalPosition.bottom))
    {
        return false;
    }

    VERIFY(pWnd->SetWindowPos(NULL, wpl.rcNormalPosition.left, wpl.rcNormalPosition.top, wpl.rcNormalPosition.right - wpl.rcNormalPosition.left, wpl.rcNormalPosition.bottom - wpl.rcNormalPosition.top, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER));

    return true;
}

/*
 * stretch window by a percent value
 * the algorithm calculates the new size for both dimensions by:
 *
 *  newWid = oldWid + (oldWid * iAddPcnt) / 100
 *
 * NOTE: iAddPcnt may even be nagtive, but it MUST be greater than -100.
 * NOTE: this function does NOT care of the min/max dimensions of a window
 *
 * The function will return false if the new size would be empty.
 */

bool cdxCDynamicWndEx::StretchWindow(int iAddPcnt)
{
    if (!IsWindow())
    {
        ASSERT(false);
        return false;
    }

    CSize szDelta = GetCurrentClientSize() + GetBorderSize();

    szDelta.cx = (szDelta.cx * iAddPcnt) / 100;
    szDelta.cy = (szDelta.cy * iAddPcnt) / 100;

    return StretchWindow(szDelta);
}


/////////////////////////////////////////////////////////////////////////////
// cdxCDynamicWndEx registry positioning
/////////////////////////////////////////////////////////////////////////////

/*
 * stores a window's position and visiblity to the registry.
 *  return false if any error occured
 */

bool cdxCDynamicWndEx::StoreWindowPosition(LPCTSTR lpszProfile, const CString& entryPrefix)
{
    if (!IsWindow() || !lpszProfile || !*lpszProfile)
    {
        ASSERT(false);
        return false;
    }

    CWnd* pWnd = Window();

    WINDOWPLACEMENT wpl;
    VERIFY(pWnd->GetWindowPlacement(&wpl));

    BOOL bVisible = pWnd->IsWindowVisible();
    int iState = REGVAL_NOSTATE;

    if (pWnd->IsIconic())
    {
        iState = REGVAL_ICONIC;
    }
    else if (pWnd->IsZoomed())
    {
        iState = REGVAL_MAXIMIZED;
    }

    CWinApp* app = AfxGetApp();

    if (!app->m_pszRegistryKey || !*app->m_pszRegistryKey)
    {
        TRACE(_T("*** NOTE[cdxCDynamicWndEx::StoreWindowPosition()]: To properly store and restore a window's position, please call CWinApp::SetRegistryKey() in you app's InitInstance() !\n"));
        return false;
    }

    return  app->WriteProfileInt(lpszProfile, entryPrefix + lpszRegVal_Valid, REGVAL_INVALID) &&    // invalidate first
    app->WriteProfileInt(lpszProfile, entryPrefix + lpszRegVal_Left, wpl.rcNormalPosition.left) && app->WriteProfileInt(lpszProfile, entryPrefix + lpszRegVal_Right, wpl.rcNormalPosition.right) && app->WriteProfileInt(lpszProfile, entryPrefix + lpszRegVal_Top, wpl.rcNormalPosition.top) && app->WriteProfileInt(lpszProfile, entryPrefix + lpszRegVal_Bottom, wpl.rcNormalPosition.bottom) && app->WriteProfileInt(lpszProfile, entryPrefix + lpszRegVal_Visible, bVisible ? REGVAL_VISIBLE : REGVAL_HIDDEN) && app->WriteProfileInt(lpszProfile, entryPrefix + lpszRegVal_State, iState) && app->WriteProfileInt(lpszProfile, entryPrefix + lpszRegVal_Valid, REGVAL_VALID);     // validate position
}

/*
 * load the registry data stored by StoreWindowPosition()
 * returns true if data have been found in the registry
 */

bool cdxCDynamicWndEx::RestoreWindowPosition(LPCTSTR lpszProfile, const CString& entryPrefix, UINT restoreFlags)
{
    if (!IsWindow() || !lpszProfile || !*lpszProfile)
    {
        ASSERT(false);
        return false;
    }

    CWnd* pWnd = Window();
    CWinApp* app = AfxGetApp();

    if (!app->m_pszRegistryKey || !*app->m_pszRegistryKey)
    {
        TRACE(_T("*** NOTE[cdxCDynamicWndEx::RestoreWindowPosition()]: To properly store and restore a window's position, please call CWinApp::SetRegistryKey() in you app's InitInstance() !\n"));
        return false;
    }

    //
    // first, we check whether the position had been saved successful any time before
    //

    if (app->GetProfileInt(lpszProfile, entryPrefix + lpszRegVal_Valid, REGVAL_INVALID) != REGVAL_VALID)
    {
        return false;
    }

    //
    // get old position
    //

    WINDOWPLACEMENT wpl;
    VERIFY(pWnd->GetWindowPlacement(&wpl));

    //
    // read registry
    //

    int iState = app->GetProfileInt(lpszProfile, entryPrefix + lpszRegVal_State, REGVAL_NOSTATE);

    //
    // get window's previous normal position
    //

    wpl.rcNormalPosition.left = app->GetProfileInt(lpszProfile, entryPrefix + lpszRegVal_Left, wpl.rcNormalPosition.left);
    wpl.rcNormalPosition.right = app->GetProfileInt(lpszProfile, entryPrefix + lpszRegVal_Right, wpl.rcNormalPosition.right);
    wpl.rcNormalPosition.top = app->GetProfileInt(lpszProfile, entryPrefix + lpszRegVal_Top, wpl.rcNormalPosition.top);
    wpl.rcNormalPosition.bottom = app->GetProfileInt(lpszProfile, entryPrefix + lpszRegVal_Bottom, wpl.rcNormalPosition.bottom);

    if (wpl.rcNormalPosition.left > wpl.rcNormalPosition.right)
    {
        long l = wpl.rcNormalPosition.right;
        wpl.rcNormalPosition.right = wpl.rcNormalPosition.left;
        wpl.rcNormalPosition.left = l;
    }
    if (wpl.rcNormalPosition.top > wpl.rcNormalPosition.bottom)
    {
        long l = wpl.rcNormalPosition.bottom;
        wpl.rcNormalPosition.bottom = wpl.rcNormalPosition.top;
        wpl.rcNormalPosition.top = l;
    }

    //
    // get restore stuff
    //

    UINT showCmd = SW_SHOWNA;

    if (restoreFlags & rflg_state)
    {
        if (iState == REGVAL_MAXIMIZED)
        {
            showCmd = SW_MAXIMIZE;
        }
        else if (iState == REGVAL_ICONIC)
        {
            showCmd = SW_MINIMIZE;
        }
    }

    //
    // use MoveWindow() which takes care of WM_GETMINMAXINFO
    //

    pWnd->MoveWindow(wpl.rcNormalPosition.left, wpl.rcNormalPosition.top, wpl.rcNormalPosition.right - wpl.rcNormalPosition.left, wpl.rcNormalPosition.bottom - wpl.rcNormalPosition.top, showCmd == SW_SHOWNA);

    if (showCmd != SW_SHOWNA)
    {
        // read updated position

        VERIFY(pWnd->GetWindowPlacement(&wpl));
        wpl.showCmd = showCmd;
        pWnd->SetWindowPlacement(&wpl);
    }

    //
    // get visiblity
    //

    if (restoreFlags & rflg_visibility)
    {
        int i = app->GetProfileInt(lpszProfile, entryPrefix + lpszRegVal_Visible, REGVAL_NOSTATE);
        if (i == REGVAL_VISIBLE)
        {
            pWnd->ShowWindow(SW_SHOW);
        }
        else if (i == REGVAL_HIDDEN)
        {
            pWnd->ShowWindow(SW_HIDE);
        }
    }

    return true;
}

